<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>微服务间的文件上传下载解决方案 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.8;
        }
        h1, h2, h3, h4 {
            font-family: 'Noto Serif SC', serif;
            font-weight: 600;
        }
        .code-block {
            background: #282c34;
            color: #abb2bf;
            border-radius: 8px;
            overflow: hidden;
        }
        .code-header {
            background: #1e2227;
            padding: 8px 12px;
            display: flex;
            align-items: center;
        }
        .code-dots {
            display: flex;
            gap: 6px;
        }
        .dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
        }
        .dot-red {
            background: #ff5f56;
        }
        .dot-yellow {
            background: #ffbd2e;
        }
        .dot-green {
            background: #27c93f;
        }
        .code-language {
            margin-left: 12px;
            color: #8993a1;
            font-size: 0.9em;
        }
        .code-body {
            padding: 16px;
            font-family: 'Menlo', 'Monaco', 'Consolas', monospace;
            font-size: 14px;
            line-height: 1.5;
            overflow-x: auto;
        }
        .card-hover {
            transition: all 0.3s ease;
        }
        .card-hover:hover {
            transform: translateY(-4px);
            box-shadow: 0 10px 25px rgba(0,0,0,0.1);
        }
        .step-circle {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 16px;
            flex-shrink: 0;
        }
        .mermaid svg {
            max-width: 100%;
            height: auto;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <div class="relative bg-gradient-to-r from-blue-600 to-indigo-800 text-white py-24 px-4 sm:px-6 lg:px-8">
        <div class="max-w-4xl mx-auto text-center">
            <h1 class="text-4xl md:text-5xl font-bold mb-6 leading-tight">微服务间的文件上传下载解决方案</h1>
            <p class="text-xl md:text-2xl opacity-90 mb-8">探索七牛云存储在现代微服务架构中的高效应用</p>
            <div class="flex justify-center space-x-4">
                <span class="px-4 py-2 bg-white bg-opacity-20 rounded-full text-sm font-medium">Java SDK</span>
                <span class="px-4 py-2 bg-white bg-opacity-20 rounded-full text-sm font-medium">Spring Boot</span>
                <span class="px-4 py-2 bg-white bg-opacity-20 rounded-full text-sm font-medium">七牛云存储</span>
            </div>
        </div>
    </div>

    <!-- Main Content -->
    <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
        <article class="prose prose-lg max-w-none">
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <p class="text-gray-600 text-lg leading-relaxed">
                        在项目中文件的上传和下载是很常见的功能，之前大部分都是上传下载都是通过浏览器来完成的。如今微服务的时代文件的上传和下载客户端不仅仅是浏览器了，而是服务和服务之间的完成的。那服务和服务之间的如何完成文件的上传和下载呢？这篇文章主要来聊聊这个。本文将接收到的文件上传到七牛云服务器。
                    </p>
                    <img src="https://cdn.nlark.com/yuque/0/2022/png/21449790/1653220152401-01ddb934-4082-43e5-b118-9d13d407ec25.png" alt="文件上传流程" class="mt-6 rounded-lg w-full object-cover">
                </div>
            </div>

            <!-- 架构图示 -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <h2 class="text-2xl font-bold mb-6 text-gray-800">架构概览</h2>
                    <div class="mermaid">
                        graph TD
                            A[客户端] -->|上传文件| B(文件服务)
                            B -->|存储| C[七牛云存储]
                            D[其他微服务] -->|调用| B
                            B -->|返回文件URL| D
                            D -->|下载文件| B
                            B -->|获取文件| C
                    </div>
                </div>
            </div>

            <!-- 导入SDK -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-blue-100 text-blue-600">
                            <i class="fas fa-download"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">导入SDK</h2>
                    </div>
                    <p class="text-gray-600 mb-6">首先需要在项目中添加七牛云Java SDK的依赖：</p>
                    <div class="code-block">
                        <div class="code-header">
                            <div class="code-dots">
                                <div class="dot dot-red"></div>
                                <div class="dot dot-yellow"></div>
                                <div class="dot dot-green"></div>
                            </div>
                            <div class="code-language">XML</div>
                        </div>
                        <div class="code-body">
                            <pre><code>&lt;dependency&gt;
    &lt;groupId&gt;com.qiniu&lt;/groupId&gt;
    &lt;artifactId&gt;qiniu-java-sdk&lt;/artifactId&gt;
    &lt;version&gt;7.13.0&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 定义文件接收DTO -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-purple-100 text-purple-600">
                            <i class="fas fa-file-code"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">定义文件接收DTO</h2>
                    </div>
                    <p class="text-gray-600 mb-6">创建一个DTO类来接收文件上传请求：</p>
                    <div class="code-block">
                        <div class="code-header">
                            <div class="code-dots">
                                <div class="dot dot-red"></div>
                                <div class="dot dot-yellow"></div>
                                <div class="dot dot-green"></div>
                            </div>
                            <div class="code-language">Java</div>
                        </div>
                        <div class="code-body">
                            <pre><code>// 接口文件上传的dto
@Data
public class ApiFileDto {
    private byte[] file; // 文件byte数组
    private String fileName; // 文件名称
}</code></pre>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 七牛云上传信息配置 -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-green-100 text-green-600">
                            <i class="fas fa-cog"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">七牛云上传信息配置</h2>
                    </div>
                    <p class="text-gray-600 mb-6">配置七牛云的认证信息和上传管理器：</p>
                    <div class="code-block">
                        <div class="code-header">
                            <div class="code-dots">
                                <div class="dot dot-red"></div>
                                <div class="dot dot-yellow"></div>
                                <div class="dot dot-green"></div>
                            </div>
                            <div class="code-language">Java</div>
                        </div>
                        <div class="code-body">
                            <pre><code>@Configuration
public class QiniuConfig {
    @Value("${qiniu.ak}")
    private String ak;

    @Value("${qiniu.sk}")
    private String sk;

    // 上传管理对象
    @Bean
    public UploadManager uploadManager() {
        UploadManager uploadManager = new UploadManager(new com.qiniu.storage.Configuration());
        return uploadManager;
    }

    // 认证对象
    @Bean
    public Auth qiqiuAuth() {
        return Auth.create(ak, sk);
    }
}</code></pre>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 文件上传接口 -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-yellow-100 text-yellow-600">
                            <i class="fas fa-upload"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">文件上传接口</h2>
                    </div>
                    <p class="text-gray-600 mb-6">实现文件上传和下载的控制器：</p>
                    <div class="code-block">
                        <div class="code-header">
                            <div class="code-dots">
                                <div class="dot dot-red"></div>
                                <div class="dot dot-yellow"></div>
                                <div class="dot dot-green"></div>
                            </div>
                            <div class="code-language">Java</div>
                        </div>
                        <div class="code-body">
                            <pre><code>@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
    @Autowired
    private UploadManager uploadManager;
    @Autowired
    private Auth auth;
    @Value("${qiniu.bucket}")
    private String bucket;
    @Value("${qiniu.host}")
    private String host;

    // 浏览器上传
    @PostMapping("/upload")
    public R fileUpload(MultipartFile file) {
        String token = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(file.getBytes(), file.getOriginalFilename(), token);
            return R.ok().put("data", host + file.getOriginalFilename());
        } catch (IOException e) {
            log.error("上传文件失败", e);
            return R.error("文件上传失败");
        }
    }

    // 接口上传，上传成功后返回文件地址
    @PostMapping("/apiUpload")
    public String apiFileUpload(@RequestBody ApiFile apiFile) {
        System.out.println("apiFile = " + apiFile);
        String token = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(apiFile.getFile(), apiFile.getFileName(), token);
            return host + apiFile.getFileName();
        } catch (IOException e) {
            log.error("上传文件失败", e);
        }
        return null;
    }

    // 接口下载
    @PostMapping("/apiDownload")
    public byte[] apiDownload(String url) {
        InputStream ips = null;
        try {
            HttpResponse response = HttpUtils.doGet(url);
            ips = response.getEntity().getContent();
            return IOUtils.toByteArray(ips);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ips != null) {
                try {
                    ips.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}</code></pre>
                        </div>
                    </div>
                </div>
            </div>

            <!-- feign接口 -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-red-100 text-red-600">
                            <i class="fas fa-exchange-alt"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">Feign接口</h2>
                    </div>
                    <p class="text-gray-600 mb-6">定义Feign客户端接口供其他服务调用：</p>
                    <div class="code-block">
                        <div class="code-header">
                            <div class="code-dots">
                                <div class="dot dot-red"></div>
                                <div class="dot dot-yellow"></div>
                                <div class="dot dot-green"></div>
                            </div>
                            <div class="code-language">Java</div>
                        </div>
                        <div class="code-body">
                            <pre><code>@FeignClient(value = "shop-file", path = "/file")
public interface FilesService {
    @PostMapping("/apiUpload")
    public String apiFileUpload(@RequestBody ApiFileDto apiFile);

    @PostMapping("/apiDownload")
    public byte[] apiDownload(@RequestParam("url") String url);
}</code></pre>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 客户端测试 -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-indigo-100 text-indigo-600">
                            <i class="fas fa-vial"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">客户端测试</h2>
                    </div>
                    <p class="text-gray-600 mb-6">测试文件上传和下载功能的示例代码：</p>
                    <div class="code-block">
                        <div class="code-header">
                            <div class="code-dots">
                                <div class="dot dot-red"></div>
                                <div class="dot dot-yellow"></div>
                                <div class="dot dot-green"></div>
                            </div>
                            <div class="code-language">Java</div>
                        </div>
                        <div class="code-body">
                            <pre><code>@Autowired
private FilesService filesService;

@Test
void contextLoads() throws Exception {
    // 1.准备要上传的文件
    String filePath = "C:\\Users\\toString\\Desktop\\20220522193617.png";
    FileInputStream ips = new FileInputStream(filePath);

    // 2.把文件的信息封装到dto
    ApiFileDto fileDto = new ApiFileDto();
    fileDto.setFile(IOUtils.toByteArray(ips));
    fileDto.setFileName("20220522193617.png");

    // 3.调用api接口上传文件，得到文件url
    String url = filesService.apiFileUpload(fileDto);
    System.out.println(url);

    // 4.根据url下载文件
    byte[] bytes = filesService.apiDownload(url);
    
    // 5.把文件输出到本地
    new FileOutputStream("./toString.png").write(bytes);
}</code></pre>
                        </div>
                    </div>
                </div>
            </div>

            <!-- 总结 -->
            <div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 card-hover">
                <div class="p-8">
                    <div class="flex items-center mb-6">
                        <div class="step-circle bg-pink-100 text-pink-600">
                            <i class="fas fa-lightbulb"></i>
                        </div>
                        <h2 class="text-2xl font-bold text-gray-800">实现总结</h2>
                    </div>
                    <div class="grid md:grid-cols-2 gap-6">
                        <div class="bg-blue-50 p-6 rounded-lg">
                            <h3 class="text-lg font-semibold text-blue-700 mb-3">优点</h3>
                            <ul class="space-y-2 text-blue-600">
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle mt-1 mr-2"></i>
                                    <span>解耦文件存储与业务逻辑</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle mt-1 mr-2"></i>
                                    <span>支持高并发文件上传下载</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle mt-1 mr-2"></i>
                                    <span>可扩展性强，支持多种存储方式</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle mt-1 mr-2"></i>
                                    <span>统一的文件访问接口</span>
                                </li>
                            </ul>
                        </div>
                        <div class="bg-purple-50 p-6 rounded-lg">
                            <h3 class="text-lg font-semibold text-purple-700 mb-3">应用场景</h3>
                            <ul class="space-y-2 text-purple-600">
                                <li class="flex items-start">
                                    <i class="fas fa-cloud-upload-alt mt-1 mr-2"></i>
                                    <span>微服务间的文件共享</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-images mt-1 mr-2"></i>
                                    <span>图片、视频等多媒体文件处理</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-file-export mt-1 mr-2"></i>
                                    <span>大数据文件处理管道</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-server mt-1 mr-2"></i>
                                    <span>分布式系统文件存储</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </article>
    </div>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-12">
        <div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
            <div class="flex flex-col items-center">
                <div class="text-2xl font-bold text-white mb-4">技术小馆</div>
                <a href="http://www.yuque.com/jtostring" class="text-blue-400 hover:text-blue-300 transition duration-150">http://www.yuque.com/jtostring</a>
                <div class="mt-6 flex space-x-6">
                    <a href="#" class="text-gray-400 hover:text-white">
                        <i class="fab fa-github text-xl"></i>
                    </a>
                    <a href="#" class="text-gray-400 hover:text-white">
                        <i class="fab fa-twitter text-xl"></i>
                    </a>
                    <a href="#" class="text-gray-400 hover:text-white">
                        <i class="fab fa-linkedin text-xl"></i>
                    </a>
                </div>
            </div>
        </div>
    </footer>

    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: true,
                htmlLabels: true,
                curve: 'basis'
            }
        });
    </script>
</body>
</html>